# coding = utf-8
import random
from abc import ABC, abstractmethod

import numpy as np

from osc05.static.constants import Commons


class AbsUncertainty(ABC):
    @abstractmethod
    def get_time_to_resume(self):
        pass

    @abstractmethod
    def get_event_source_name(self):
        pass


class CraneUncertainty(AbsUncertainty):
    RT_MEAN = 4.0  # Avg. repairing time in hours
    RT_SIGMA = 2.0  # Sigma of repairing time

    def __init__(self, env, seed=1, name="CRANE", break_mean=3000):
        self.env = env
        self.name = name
        self.break_mean = break_mean
        random.seed(seed)
        # start the break process
        env.process(self.crane_failure_interrupt_erection())

    def get_event_source_name(self):
        return self.name

    def get_time_to_resume(self):
        # return to the interrupted
        # time_to_fix_crane = random.normalvariate(CraneUncertainty.RT_MEAN,
        #                                          CraneUncertainty.RT_SIGMA)
        time_to_fix_crane = np.random.normal(CraneUncertainty.RT_MEAN,
                                             CraneUncertainty.RT_SIGMA)
        return time_to_fix_crane

    def get_time_to_next_failure(self):
        # random.seed(1)
        """Return time until next failure for a machine."""
        return random.expovariate(self.break_mean)

    def crane_failure_interrupt_erection(self):
        """Break the machine every now and then."""
        print("breaking begins")
        while True:
            # get working time until next failure for the crane.
            normal_working_time = self.get_time_to_next_failure()
            # pass the time to simulate work in normal condition
            yield self.env.timeout(normal_working_time)
            print("Crane is broken", normal_working_time, " after last break, @", self.env.now)
            # check all the main processes
            for process in Commons.main_process_list:
                # if the process is being interrupted, it cannot be interrupted again.
                if process in Commons.being_interrupted_process_list:
                    print("@", self.env.now, "fails to interrupt, the current process is being interrupted")
                else:
                    #  if a process is currently working
                    if process.is_alive:
                        # print("break a activity")
                        process.interrupt(self)
                        # append the process to Commons.being_interrupted_process_list
                        Commons.being_interrupted_process_list.append(process)
                    else:
                        print("Not Alive")
